/*
 * Author: xiguan
 * Mail: xiguan.teng@qq.com
 * -----
 * Last Modified: 2022-05-27 21:59:35
 * Modified By: xiguan
 * -----
 * Copyright (c) 2022 XiGuan Inc.
 * 
 * No Bug No error No warning.
 */



#include <bits/stdc++.h>
#include <linux/limits.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h>
#include <unistd.h>
#include <dirent.h>
#include <pwd.h>
#include <grp.h>
#include <time.h>
// #include <errno.h>


using namespace std;

enum param{param_none, param_a, param_l, maxrowlen=80}; // 无参数 -a -l 一行最多显示最多字符数
int row_leave_len = maxrowlen; // 一行剩下的字符数 用于对齐
int file_maxnamelen; // 某目录下最长文件名长度


void message_err(const char *msg, int flag = 0){
    if(flag != 0){
        perror(msg);
    } else{
        fputs(msg, stderr);
        fputc('\n', stderr);
    }
    exit(1);
}
/**
 * @brief 展示目录
 * 
 * @param model 
 * @param path 
 */
void display_dir(int model, char *path);
/**
 * @brief 展示文件
 * 
 * @param model 
 * @param path 
 */
void display(int model, char *path);
/**
 * @brief 无详细打印
 * 
 * @param path 
 */
void display_single(char *path);
/**
 * @brief 详细打印
 * 
 * @param buf 
 * @param path 
 */
void display_attribute(struct stat buf, char *path);

int main(int argc, char *argv[]){

    int model = 0;
    char path[PATH_MAX+1];
    struct stat stat_buf;

    cout << "命令行参数：" << endl;
    for(int i = 1; i < argc; ++ i){
        cout << argv[i] << endl;
    }
    
    if(argc == 1){
        model = 0;
        strcpy(path, "./"); //当前目录
        path[2] = '\0';
    } else if(argc == 2){
        strcpy(path, "./"); //当前目录
        path[2] = '\0';
        if(argv[1][0] == '-'){
            for(int i = 0; i < strlen(argv[1]); i ++){
                if(argv[1][i] == '-')
                    continue;
                if(argv[1][i] == 'a' && model < 4){
                    model += param_a;
                } else if(argv[1][i] == 'l' && model < 4){
                    model += param_l;
                } else{
                    printf("Uasge : %s or %s <option> or %s <option> <path>\n", argv[0],argv[0],argv[0]);
                    message_err("param error");
                }
            }
        } else{
            model = 0;
            strcpy(path, argv[1]); // 命令行参数
        }
    } else if(argc == 3){
        for(int i = 0; i < strlen(argv[1]); i ++){
            if(argv[1][i] == '-')
                continue;
            if(argv[1][i] == 'a' && model < 4){
                model += param_a;
            } else if(argv[1][i] == 'l' && model < 4){
                model += param_l;
            } else{
                printf("Uasge : %s or %s <option> or %s <option> <path>\n", argv[0],argv[0],argv[0]);
                message_err("param error");
            }
        }
        strcpy(path, argv[2]); // 命令行参数
    } else{
        printf("Uasge : %s or %s <option> or %s <option> <path>\n", argv[0],argv[0],argv[0]);
        message_err("param error");
    }


        
    if(stat(path, &stat_buf) == -1){
        message_err("stat: ", -1);
    }

    // 是一个目录
    if(S_ISDIR(stat_buf.st_mode)){
        // 目录最后不是/就加上
        if(path[strlen(path)-1] != '/'){
            path[strlen(path)] = '/';
            path[strlen(path)+1] = '\0';
        } else{
            path[strlen(path)] = '\0';
        }

        display_dir(model, path);
    } else{
        display(model, path);
    }



    return 0;
}

void display_dir(int model, char *path){
    DIR *dir;
    struct dirent *dir_ptr;
    int count = 0; //文件总数
    char filenames[256][PATH_MAX+1], tmp[PATH_MAX+1];

    if((dir = opendir(path)) == NULL){
        message_err("opendir: ", -1);
    }

    while((dir_ptr = readdir(dir)) != NULL){
        if(file_maxnamelen < strlen(dir_ptr->d_name))
            file_maxnamelen = strlen(dir_ptr->d_name);
        count ++;
    }
    // cout << count << endl;
    closedir(dir);

    if(count > 256){
        message_err("too many files under this dir");
    }

    int len = strlen(path);

    dir = opendir(path);
    for(int i = 0; i < count; i ++){
        if((dir_ptr = readdir(dir)) == NULL){
            message_err("readdir: ");
        }
        strncpy(filenames[i], path, len);
        filenames[i][len] = '\0';
        strcat(filenames[i], dir_ptr->d_name);
        filenames[i][len+strlen(dir_ptr->d_name)] = '\0';
    }

    // 对文件名进行排序

    for(int i = 0; i < count-1; ++ i){
        for(int j = 0; j < count-1-i; ++ j){
            if(strcmp(filenames[j], filenames[j+1]) > 0){
                strcpy(tmp, filenames[j]);
                tmp[strlen(filenames[j])] = '\0';
                strcpy(filenames[j], filenames[j+1]);
                filenames[j][strlen(filenames[j+1])] = '\0';
                strcpy(filenames[j+1], tmp);
                filenames[j+1][strlen(tmp)] = '\0';
            }
        }
    }

    for(int i = 0; i < count; ++ i){
        display(model, filenames[i]);
    }
    closedir(dir);
    if((model&param_l) == 0){
        puts("");
    }
}

void display(int model, char *path){
    struct stat stat_buf;
    char name[NAME_MAX+1];
    int i, j;

    //从路径中提取文件名
    
    for(i = 0, j = 0; i < strlen(path); ++ i){
        if(path[i] == '/'){
            j = 0;
            continue;
        }
        name[j ++] = path[i];
    }
    name[j] = '\0';

    if(lstat(path, &stat_buf) == -1){
        message_err("lstat: ", -1);
    }

    switch(model){
        case param_none:
            if(name[0] != '.'){
                display_single(name);
            }
            break;
        case param_a:
            display_single(name);
            break;
        case param_l:
            if(name[0] != '.'){
                display_attribute(stat_buf, path);
                printf(" %-s\n", name);
            }
            break;
        case param_a+param_l:
            display_attribute(stat_buf, path);
            printf(" %-s\n", name);
            break;
        default:
            break;
    }
    
}

void display_single(char *path){
    int len;

    if(row_leave_len < file_maxnamelen){
        printf("\n");
        row_leave_len = maxrowlen;
    }

    len = strlen(path);
    len = file_maxnamelen-len;

    printf("%-s", path);
    for(int i = 0; i < len; ++ i){
        cout << " ";
    }
    
    printf("  ");
    
    row_leave_len -= file_maxnamelen-2;
}

void display_attribute(struct stat buf, char *path){
    char time_buf[32];
    struct passwd *psd; //获取所有者的用户名
    struct group *grp; //获取所属组组名

    //获取打印文件类型
    if(S_ISLNK(buf.st_mode)){
        cout << "l";
    } else if(S_ISREG(buf.st_mode)){
        cout << "-";
    } else if(S_ISDIR(buf.st_mode)){
        cout << "d";
    } else if(S_ISCHR(buf.st_mode)){
        cout << "c";
    } else if(S_ISBLK(buf.st_mode)){
        cout << "b";
    } else if(S_ISSOCK(buf.st_mode)){
        cout << "s";
    } else if(S_ISFIFO(buf.st_mode)){
        cout << "f";
    }

    //打印文件所有者权限
    if(buf.st_mode&S_IRUSR){
        cout << "r";
    } else{
        cout << "-";
    }
    if(buf.st_mode&S_IWUSR){
        cout << "w";
    } else{
        cout << "-";
    }
    if(buf.st_mode&S_IXUSR){
        cout << "x";
    } else{
        cout << "-";
    }

    //打印文件所属组权限
    if(buf.st_mode&S_IRGRP){
        cout << "r";
    } else{
        cout << "-";
    }
    if(buf.st_mode&S_IWGRP){
        cout << "w";
    } else{
        cout << "-";
    }
    if(buf.st_mode&S_IXGRP){
        cout << "x";
    } else{
        cout << "-";
    }
    
    //打印文件其他用户权限
    if(buf.st_mode&S_IROTH){
        cout << "r";
    } else{
        cout << "-";
    }
    if(buf.st_mode&S_IWOTH){
        cout << "w";
    } else{
        cout << "-";
    }
    if(buf.st_mode&S_IXOTH){
        cout << "x";
    } else{
        cout << "-";
    }

    cout << "  ";
    
    psd = getpwuid(buf.st_uid);
    grp = getgrgid(buf.st_gid);


    
    printf("%4ld ", buf.st_nlink); //打印文件连接数
    printf("%-8s", psd->pw_name); //打印用户名
    printf("%-8s", grp->gr_name); //打印组名
    printf("%6ld", buf.st_size); //打印大小
    strcpy(time_buf, ctime(&buf.st_mtime));
    time_buf[strlen(time_buf)-1] = '\0';
    printf("  %s", time_buf);

}






